package turtle

/*
    TODO

    (DONE) literal datatypes
    (DONE) @base
    (DONE) lists
    (DONE) """-strings
    (DONE) \escapes
    (DONE) decimals
    (DONE) doubles = decimal followed by exponent
    blank nodes _:NAME
    generalised names (ie not just A-Za-z)
    hex escapes (w/ decoding)
    decent error messages (incl line/col number)
    (DONE) @prefix doing something
    @base doing something
    and any sort of storage for the triples.
*/

import "fmt"
import "trigger.googlecode.com/hg/lib/rdf"
import "trigger.googlecode.com/hg/lib/uri"

type Token struct {
    Class Kind
    Spelling string
}

// A Sink consumes triples. It can generate BNodes and
// handle prefixes. Probably it should become a part of
// Graph.
type Sink interface {
    Add(rdf.Node, rdf.Node, rdf.Node)
    BNode() rdf.Node
    SetPrefix( string, uri.Type )
    ExpandPrefix(qName string) string 
}

func Main(h Sink, all string) {
    stream := make(chan(Token))
    // h := printingHandler{make(map[string]string)}
    go tokeniseString(all, stream)
    parse(h, stream)
}

type TOKENS struct {
    stream chan(Token)
    current Token
}

func (t *TOKENS) advance() {
    if t.current.Class != EOF { t.current = <- t.stream }
}

func newTokens(stream chan(Token)) *TOKENS {
    return &TOKENS{stream, <-stream}
}

func parse(h Sink, stream chan(Token)) {
    t := newTokens(stream)
    parseTop(h, t)
}

func parseTop(h Sink, t *TOKENS) {
    for {
        switch t.current.Class {
            case PREFIX:
                t.advance()
                qname := t.current.Spelling
                t.advance()
                def := t.current.Spelling
                t.advance()
                t.advance()
                h.SetPrefix(qname, uri.Type(def))

            case BASE:
                t.advance()
                t.advance()
                t.advance()

            case EOF:
                return

            default:
                triples(h, t)
            }
    }
}

func triples(h Sink, t *TOKENS) {
    s := subject(h, t)
    predications(h, t, s)
}

func predications(h Sink, t *TOKENS, s rdf.Node) {
    for {
        if t.current.Class == SEMI { t.advance() }
        if t.current.Class == URI || t.current.Class == QNAME || t.current.Class == A {
            p := subject(h, t)
            objects(h, t, s, p)
        } else {
            if t.current.Class == DOT { t.advance() }
            return
        }
    }
}

func subject(h Sink, t *TOKENS) rdf.Node {
    switch t.current.Class {

        case STRING:
            lexicalForm := t.current.Spelling
            t.advance()
            if t.current.Class == LANGUAGE {
                language := t.current.Spelling
                t.advance()
                return rdf.AsLiteral(lexicalForm, language, "")
            } else if t.current.Class == DATATYPE {
                dataType := t.current.Spelling
                t.advance()
                return rdf.AsLiteral(lexicalForm, "", dataType)
            }
            return rdf.AsLiteral(lexicalForm, "", "")

        case URI:
            result := rdf.AsURI(t.current.Spelling)
            t.advance()
            return result;
        
        case NUMBER:
            result := rdf.AsLiteral(t.current.Spelling, "", "SOME_NUMERIC")
            t.advance()
            return result

        case QNAME:
            result := rdf.AsURI(h.ExpandPrefix(t.current.Spelling))
            t.advance()
            return result 

        case LPAR:
            t.advance()
            if t.current.Class == RPAR { t.advance(); return rdf.NIL }
            s := h.BNode()
            here := s
            for {
                x := subject(h, t)
                h.Add( here, rdf.FIRST, x )
                if t.current.Class == RPAR { break }
                r := h.BNode()
                h.Add( here, rdf.REST, r )
                here = r
            }
            t.advance()
            h.Add( here, rdf.REST, rdf.NIL )
            return s;

        case LBOX:
            s := h.BNode()
            t.advance()
            predications(h, t, s);
            if t.current.Class == RBOX { t.advance()
            } else { fmt.Println( "expected RBOX, got ", t.current.Spelling ) }
            return s

        default:
            Spelling := t.current.Spelling
            fmt.Println( "illegal start-of-subject: ", Spelling)
            t.advance()
            return rdf.AsLiteral(Spelling, "", "WRONG")
    }
    return rdf.AsLiteral(t.current.Spelling, "", "WRONG") 
}

func objects(h Sink, t *TOKENS, s, p rdf.Node) {
    for {
        h.Add( s, p, subject(h, t) )
        if t.current.Class != COMMA { return }
        t.advance()
    }
}


